/*!
 * Chart JavaScript Library v0.5
 * Copyright 2011-2012, Louis Chen
 * Lastupdatedby Louis Chen 06/Jan/2012 HKT
 *
 */
(function(window,undefined){
	var document = window.document,
	// Check if a string has a non-whitespace character in it
	rnotwhite = /\S/,
	//Save a reference to some core methods
	toString = Object.prototype.toString,
	indexOf = Array.prototype.indexOf,
	// Matches string for close 
	rcls = /\d{1,2}\/\d{1,2}\/\d{4}(?:(?:\s*\d{2}:\d{2}))*,(?:\d*\.\d{4},){3}(\d*\.\d{4})/g,
	// Matches string for RSI	(RSI19, RSI20)
	rsi = /\d{1,2}\/\d{1,2}\/\d{4}(?:(?:\s*\d{2}:\d{2}))*,(?:(?:\d*\.\d*)?,){9}(\d*\.\d*)/g,
	// Matches string for SAM (SAM19, SAM20)
	sam = /\d{1,2}\/\d{1,2}\/\d{4}(?:(?:\s*\d{2}:\d{2}))*,(?:\d*\.\d{4},){4}(\d*\.\d{4})/g,
	// Matches string for close of five years
	y5ex = /\d*\/\d*\/\d{4},(\d*\.\d*)/g,
	// Matches string for date dd/mm/yyyy 
	rdate = /(\d{2})\/(\d{2})\/(\d{4})\s*(\d{2}:\d{2})?/,
	// Matches numeric
	numeric = /(\d*)\.?(\d*)/;
	
	var supportCanvas = (function(){
		var canvas = document.createElement('canvas');
		return canvas.getContext&&canvas.getContext('2d');
	})();
	// This function comes from jquery
	// Evaluates a script in a global context
	// Workarounds based on findings by Jim Driscoll
	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
	function globalEval(data){
		if(data && rnotwhite.test(data)){
			// We use execScript on Internet Explorer
			// We use an anonymous function so that context is window
			// rather than jQuery in Firefox
			(window.execScript||function(data){
				window['eval'].call(window,data);
			})(data);
		}
	}
	// cross-domain
	// Using json padding
	// Append script element to head
	// Remove the script tag when the script have ran already
	function scriptTransport(url){
		var script, head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
		return {
			send : function(callback){
				script  = document.createElement('script');
				script.async = "async";
				script.charset = "utf-8";
				script.src =  url;
				// Attach handlers for all browsers
				script.onload = script.onreadystatechange = function(isAbort){
					if(isAbort||!script.readyState||/loaded|complete/.test( script.readyState )){
						//release
						script.onload = script.onreadystatechange = null;
						// Remove the script
						if(head && script.parentNode){
							head.removeChild(script);
						}
						//release
						script = undefined;
						//
						if(!isAbort){
							callback();
						}
					}
				};
				// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
				// This arises when a base node is used (#2709 and #4378).
				head.insertBefore( script, head.firstChild );				
			},
			abort : function(){
				if(script){
						script.onload( 0, 1 );
				}
			}
		}
	}
	// constructor the json padding
	function jsonPadding(options){
		if(!options) return;
		var transport,
		jsonpCallback = options.jsonpCallback||'validateMKDChartResponse',
		previous = window[ jsonpCallback ],
		url = options.url,
		data = options.data||"";  //recommand json format
		if(url){
			if(data && typeof data === "object"){
				var tmp = "";
				for(var name in data){
					tmp += name + "=" + data[name] + "&";
				}
				if(tmp){
					data = tmp.replace(/&$/,tmp);
					delete tmp;
				}
			}
			//url += (/\?/.test( url ) ? "&" : "?" ) + data + "&callback=" +  jsonpCallback;
			transport = new scriptTransport(url);
		}
		//install callback
		// is it mandatory???
		window[jsonpCallback]  = function(repsonse){
			if(toString.call(previous) === "[object Function]"){
				previous(repsonse);
			}else{
				// Garbage collect
				window[jsonpCallback] = undefined;
				try {
					delete window[jsonpCallback];
				} catch( jsonpError ) {}								
			}
		};
		if(transport){
			transport.send(window[jsonpCallback]);
		}
	}
	// extend
	// 
	function extend(){
		var options, name, src, copy, copyIsArray, clone,
			target = arguments[0] || {},
			i = 1,
			length = arguments.length,
			deep = false;
			// will not handle deep copy here
			// Handle case when target is a string or something (possible in deep copy)
			if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
				target = {};
			}
			// extend jQuery itself if only one argument is passed
			if ( length === i ) {
				target = this;
				--i;
			}
			for(;i<length; i++){
				// Only deal with non-null/undefined values
				if ( (options = arguments[ i ]) != null ) {
					// Extend the base object
					for ( name in options ) {
						src = target[ name ];
						copy = options[ name ];
						// Prevent never-ending loop
						if ( target === copy ) {
							continue;
						}
						if( copy !== undefined ){
							target[ name ] = copy;
						}
					}
				}
			}
		// Return the modified object	
		return target;											
	}
	// extend Array prototype
	// return the max value in array
	Array.prototype.max = function(){
		return Math.max.apply(null,this);
	}
	// return the min value in array
	Array.prototype.min = function(){
		return Math.min.apply(null,this);
	}
	
	/*@hChart Api
	 * Canvas support needed
	 * HTML5 support for the best
	*/
	var hChart = (function(){
		// Define a local copy of hChart
		var hChart = function(canvas,options){
			// The hChart object is actually just the init constructor 'enhanced'
			return new hChart.api.init(canvas,options);
		};
		
		//init api
		hChart.api = hChart.prototype = {
			constructor : hChart,
			// The current version being used
			version : '0.5',
			//
			dateexpr : /(\d{4})(\d{2})(\d{2})\s*(\d{2}:\d{2})?/,
			// The default settings
			settings : {
				//dayofweek display
				dayofweek : 1,
				//set up the period duration
				periodDuration : {
					'1D' : 216E5,
					'1W' : 864E5,
					'2W' : 6048E5,
					'1M' : 6048E5,
					'3M' : 26784E5,
					'6M' : 26784E5,
					'1Y' : 53568E5,
					'2Y' : 80352E5,
					'5Y' : 316224E5
				},
				// Map over step
				stepover : {
					'1D' : 10,
					'1W' : 23,
					'2W' : 23,
					'1M' : 23,
					'3M' : 15,
					'6M' : 15,
					'1Y' : 15,
					'2Y' : 15,
					'5Y' : 15
				},
				// margin of label text
				margin : {
					'1D' : 10,
					'1W' : 23,
					'2W' : 23,
					'1M' : 23,
					'3M' : 15,
					'6M' : 15,
					'1Y' : 15,
					'2Y' : 15,
					'5Y' : 15					
				},
				// Map over labels				
				spot : {
					'1D' : /(\d{2}\/\d{2}\/\d{4})\s*((?:12|06|00|18):00)/,
					'1W' : /(\d{1,2}\/\d{1,2}\/\d{4})\s*00:00/,
					'2W' : 'dayofweek', //
					'1M' : 'dayofweek', //
					'3M' : /(?:01|02|03|04|05|06|07)\/\d{2}\/\d{4}/,
					'6M' : /(?:01|02|03|04|05|06|07)\/\d{2}\/\d{4}/,
					'1Y' : /(?:01|02|03|04|05|06|07)\/\d{2}\/\d{4}/,
					'2Y' : /(?:01|02|03|04|05|06|07)\/\d{2}\/\d{4}/,
					'5Y' : /(?:01|02|03|04|05|06|07)\/01\/\d{4}/
				},
				//
				labels : {
					supportlevel : 'Support Level',
					resistancelevel : 'Resistance Level'
				},
				SMA : {
					color : '#8A2BE2',
					lineWidth : 2
				},
				RSI : {
					height : 50,
					color : '#8A2BE2',
					lineWidth : 1					
				},
				W5Y : {
					linecolor : '#000',
					fillcolor : '#FF4D4D',
					background : null
				},
				gridcolor : 'rgba(100,100,100,0.3)',
				closeLineColor : 'rgba(100,100,100,0.8)',
				closeFillColor : 'rgba(100,100,100,0.3)',
				trace : false,
				bottomMargin : 13
			},
			RSI : {
				rows : 5
			},
			W5Y : {
				rows : 5
			},
			init : function(canvas,options){
				if(!canvas) return this;
				var context = document.getElementById(canvas);
				if(!context) return this;
				this.canvas = context;
				this.ctx = context.getContext('2d'); //only support 2D here
				this.__w = context.width;
				this.__h = context.height;
				this.height = this.__h; //back up
				this.left = this.canvas.offsetLeft;
				this.top = this.canvas.offsetTop;
				//
				this.settings = extend(this.settings,options||{}),data = this.settings.data;
				this.root = document.getElementById(this.settings.root);
				this.fiveYear = document.getElementById(this.settings.fiveYear);
				if(!data){
					this.__canvas = context.cloneNode(true);
					return this;
				}
				this.draw();
				return this;
			},
			/**
			 *Parese the string to date, the format must be YYYYMMDD
			 *@version0.5
			 *	
			*/
			parseDate : function(str){
				if(!str) return;
				var match = str.match(this.dateexpr),date;
				if(match&&match[0]&&match[1]&&match[2]&&match[3]&&!match[4]){
					date = match[1] + '/' + match[2] + '/' + match[3];
				}else if(match&&match[0]&&match[1]&&match[2]&&match[3]&&match[4]){
					date = match[1] + '/' + match[2] + '/' + match[3] + ' ' + match[4];
				}
				if(date){
					var d = new Date(date);
					if(d){
						return d.getTime();
					}
				}
				return;
			},
			roundTo : function(num,unit){
				if(0 > unit) return num;
				unit = Math.pow(10,unit);
				return Math.round(num * unit) / unit; 
			},
			fitToBounds : function(a,b,c){
				a < b && (a=b);
				a > c && (a=c);
				return a;
			},
			formatNumber : function(num,format){
				num = this.roundTo(num,format.precision);
				var decimalSeparator = format.decimalSeparator,
						thousandsSeparator = format.thousandsSeparator,
						isMinus = 0 > num ? '-' : '',
						num = Math.abs(num),
						numStr = num.toString();
				if(numStr.indexOf('e') == -1){
					for(var numArr = numStr.split(decimalSeparator), i = "", k = numArr[0].toString(), j = k.length; 0 <= j; j -= 3){
						i = j != k.length ? 0 != j ? k.substring(j - 3, j) + thousandsSeparator + i : k.substring(j - 3, j) + i : k.substring(j - 3, j);
					}
					numArr[1] && (i = i + decimalSeparator + numArr[1]);
					format.precision > 0 && "0" != i && (i = this.addZeros(i,decimalSeparator,format.precision));					
				}else{
					i = h;
				}
				i = isMinus + i;
				return i;
			},
			addZeros : function(){
				var num = arguments[0],decimalSeparator = arguments[1],precision = arguments[2];
				num = num.split(decimalSeparator);
				void 0 == num[1] && precision > 0 && (num[1] = "0");
				return num[1].length < precision ? (num[1] += "0",this.addZeros(num[0] + decimalSeparator + num[1],decimalSeparator,precision)) : void 0 != num[1] ? num[0] + decimalSeparator + num[1] : num[0];
			},
			getCoordinate : function(row){
				if(isNaN(row)) return NaN;
				var y = (this.__h - this.settings.bottomMargin) - this.stepWidth * (row - this.min);
				y = Math.round(10 * y) / 10;
				return y;
			},
			timeZoom : function(startTime, endTime, period){
				var data = this.chartData,len = 0;
				if(data && (len = data.length) > 0){
					var minDuration = this.settings.periodDuration[period],
					firstTime = this.getTimeFromData(data[0]);
					this.firstTime = firstTime;
					var lastTime = this.getTimeFromData(data[len - 2]);
					this.lastTime = lastTime;
					startTime ||(startTime = firstTime);
					endTime || (endTime = lastTime);
					startTime > lastTime && (startTime = lastTime);
					endTime < firstTime && (endTime = firstTime);
					startTime < firstTime && (startTime = firstTime);
					endTime > lastTime && (endTime = lastTime);
					endTime < startTime && (endTime = startTime + minDuration);
					this.startTime = startTime;
					this.endTime = endTime;
					var start = 0;
					var end = len-2;
					this.start = this.fitToBounds(start,0,len-2);
					this.end = this.fitToBounds(end,0,len-2);
				}
			},
			getTimeFromData : function(data){
				return this.parseDate(data.split(',')[0]);
			},
			formatTimeToDate : function(time){
				var d = new Date(time);
				return (d.getMonth() + 1) < 10 ? ("0" + (d.getMonth()+1)) + "/" + d.getFullYear() : (d.getMonth()+1) + "/" + d.getFullYear(); 
			},
			// date string dd/mm/yyyy
			correctDate : function(date){
				var arr = date.split('/');
				return arr[1] + '/' + arr[0] + '/' + arr[2];
			},
			drawRows : function(){
				this.ctx.beginPath();
				this.ctx.strokeStyle= this.settings.gridcolor;
				this.ctx.fillStyle = "black";
				this.ctx.font = "11pt Helvetica";
				var rowLength = this.rows.length;
				for (var i = 0; i < rowLength; i++){
					var y = this.rows[i].y,label = this.rows[i].label;
					this.ctx.fillText(label,5, y - 5);
					this.ctx.moveTo(0, y);
					this.ctx.lineTo(this.__w, y);
				}
				this.ctx.stroke();
				if(this.rsi){
					var cxt = this.rsi.getContext('2d');
					cxt.beginPath();
					cxt.strokeStyle= this.settings.gridcolor;
					cxt.fillStyle = "black";
					for(i=0; i < this.RSI.rows; i++){
						var y = (i+1) * this.RSI.rowHigh;
						cxt.fillText(this.RSI.bottom + (this.RSI.rows - 1 - i) * this.RSI.step, this.__w - 13,y - 2);
						cxt.moveTo(0,y);
						cxt.lineTo(this.__w,y);
					}
					cxt.stroke();
				}				
			},
			render : function(){
				this.ctx.beginPath();
				this.ctx.strokeStyle = this.settings.closeLineColor;
				var first = this.chartData[0].split(','),i = 1,duration = this.settings.periodDuration[this.period],
				spot = this.settings.spot[this.period],rsiCtx;
				this.ctx.moveTo(0, this.getCoordinate(first[4]));
				if(first[5]){
					this.smaCtx.beginPath();
					this.smaCtx.strokeStyle = this.settings.SMA.color;
					this.smaCtx.lineWidth = this.settings.SMA.lineWidth;
					this.smaCtx.moveTo(0, this.getCoordinate(first[5]));
					//in case only one data
					if(this.chartDataSize == 1){
						this.smaCtx.lineTo(this.__w, this.getCoordinate(first[5]));
					}
				}
				if(this.rsi){
					rsiCtx = this.rsi.getContext('2d');
					rsiCtx.beginPath();
					rsiCtx.strokeStyle = this.settings.RSI.color;
					rsiCtx.lineWidth = this.settings.RSI.lineWidth;
					rsiCtx.moveTo(0,this.rsi.height - ((first[10] - 0 - this.RSI.bottom) * this.RSI.rowHigh/this.RSI.step));
				}							
				for(; i < this.chartDataSize; i++){
					var x = this.xUnit * i,
						  row = this.chartData[i].split(','),
						  RSIX = row[10];
					var match = row[0].match(rdate);
					if(match[4]){
						//1D or 1W
						if(this.period === '1D'){
							var m = row[0].match(spot);
							if(m&&m[0]&&m[1]&&m[2]){
								this.colsArr[this.colsArr.length] = {
									x : i * this.xUnit,
									label : m[2] == "00:00" ? m[1] : m[2],
									stepover : m[2] == "00:00" ? 23 : this.settings.stepover[this.period]
								};								
							}
						}else{
							//1W
							var m = row[0].match(spot);
							if(m&&m[0]&&m[1]){
								this.colsArr[this.colsArr.length] = {
									x : i * this.xUnit,
									label : m[1]
								};								
							}
						}
					}else{
						var date = new Date(this.parseDate(match[3] + match[2] + match[1]));
						if(spot == 'dayofweek'){
							if(date.getDay() == this.settings[spot]){
								this.colsArr[this.colsArr.length] = {
									x : i * this.xUnit,
									label : row[0]
								};
							}
						}else{
							var m = row[0].match(spot);
							if(m&&m[0]){
								this.colsArrTemp[this.colsArrTemp.length] = new Date(this.correctDate(m[0])).getTime();
								this.valueAxes[this.valueAxes.length] = i * this.xUnit;
							}
						}
					}
					this.ctx.lineTo(x,this.getCoordinate(row[4]));
					if(this.smaCtx){
						this.smaCtx.lineTo(x, this.getCoordinate(row[5]));
					}
					if(RSIX){
						rsiCtx.lineTo(x, this.rsi.height - ((RSIX - 0 - this.RSI.bottom) * this.RSI.rowHigh/this.RSI.step));
					}
				}
				//fill the area
				this.ctx.lineTo(this.__w, this.realHeight);
				this.ctx.lineTo(0, this.realHeight);
				this.ctx.fillStyle=this.settings.closeFillColor;
				this.ctx.fill();
				this.ctx.stroke();
				this.smaCtx && this.smaCtx.stroke();
				(rsiCtx&&rsiCtx.stroke());
				//release contexts
				rsiCtx = null;
			},
			drawCols : function(){
				this.ctx.beginPath();
				this.ctx.strokeStyle= this.settings.gridcolor;
				this.ctx.fillStyle = "black";
				this.ctx.font = "9px Helvetica";
				var i = 0, len = this.colsArr.length,margin = this.settings.margin[this.period],stepover = this.settings.stepover[this.period];
				if(len == 0){
					var duration = this.settings.periodDuration[this.period],date,l = this.colsArrTemp.length,dateofmonth,dayofweek,index = 0,target;
					//864E5
					this.startTime = this.colsArrTemp[0];
					//
					for(var i = this.startTime; i <= this.endTime; i += duration){
						date = new Date(i),dateofmonth = date.getDate(),dayofweek = date.getDay(),target = null,dateofmonth_backup = dateofmonth,i_backup = date;
						//adjust in case the duration over
						while(dateofmonth > 1){
							dateofmonth--;
						}
						target = date.setDate(dateofmonth);
						i = target;
						dayofweek = date.getDay();
						//handle to skip SAT/SUN
						if(dayofweek != 0 && dayofweek !=6){
							index = this.colsArrTemp.indexOf(i);
							target = i;
						}else if(dayofweek == 0){
							//+1
							target = date.setDate(dateofmonth + 1);
							index = this.colsArrTemp.indexOf(target);
						}else if(dayofweek == 6){
							//+2
							target = date.setDate(dateofmonth + 2);
							index = this.colsArrTemp.indexOf(target);
						}
						while(index == -1 && dateofmonth_backup!=7){
							index = this.colsArrTemp.indexOf(i_backup.getTime());
							target = i_backup.getTime();
							i_backup.setDate(i_backup.getDate() + 1);
							dateofmonth_backup = i_backup.getDate();
						}
						target && (this.colsArr[this.colsArr.length] = {'x':this.valueAxes[index],'label': this.formatTimeToDate(target)});
					}
					len = this.colsArr.length;
					this.colsArrTemp.length = 0;
					this.valueAxes.length = 0;
				}
				for(var i = 0; i < len; i++){
					var obj = this.colsArr[i];
					this.ctx.moveTo(obj.x,0);
					this.ctx.lineTo(obj.x,this.realHeight);	
					//handle 1D case
					if(obj.stepover){
						stepover = obj.stepover;
						margin = stepover;
					}
					//
					if(i > 0 && obj.x - this.colsArr[i-1].x <= margin * 2){
						continue;
					}
					if(this.__w - obj.x >= margin && obj.x - 0 >= margin){
						this.ctx.fillText(obj.label, obj.x - stepover, this.realHeight + this.settings.bottomMargin);
					}
				}
				this.ctx.stroke();
			},
			drawDottedline : function(type){
				if(!type){
					throw "You must define the type of dotted line!";
				}
				var y,spot,label;		
				if(type == "S"){
					y = this.getCoordinate(this.supportlevel);
					spot = this.supportlevel + "";
					label = this.settings.labels['supportlevel'];
				}else if(type == "R"){
					y = this.getCoordinate(this.resistancelevel);
					spot = this.resistancelevel + "";
					label = this.settings.labels['resistancelevel'];					
				}
				if(!y||!spot||!label){
					throw "Please check the type you pass in!";
				}
				//create overlay canvas
				if(!this.dottedCanvas){
					this.dottedCanvas = document.createElement("canvas");
					this.dottedCanvas.width = this.__w;
					this.dottedCanvas.height = this.__h;
					this.dottedCanvas.style.position = 'absolute';
					this.dottedCanvas.style.left = this.left;
					this.dottedCanvas.style.top = this.top;
					this.root.appendChild(this.dottedCanvas);
				}
				var ctx = this.dottedCanvas.getContext('2d');			
				ctx.beginPath();
				ctx.strokeStyle= "red";
				ctx.fillStyle = "red";
				ctx.font = "9pt Helvetica";				
				var x = 0;
				while(x < this.__w){
					ctx.moveTo(x, y);
					ctx.lineTo(x+10,y);
					x += 20;
				}
				ctx.fillText(label + " : " + spot,50,y - 5);
				ctx.stroke();
			},
			clearDottedline : function(){
				if(this.dottedCanvas){
					this.root.removeChild(this.dottedCanvas);
					this.dottedCanvas = null;
				}
			},
			reset : function(options){
				//if false only reset 
				this.settings = extend(this.settings,options);
				this.ctx.clearRect(0,0,this.__w,this.__h);
				if(this.settings.width){
					this.__w = this.settings.width;
				}
				this.canvas.width = this.__w;
				this.canvas.height = this.height;
				this.__h = this.height;
				this.clearDottedline();
				if(this.rsi){
					this.__h -= this.rsi.height;
					this.rsi.width = this.__w;
					this.rsi.getContext('2d').clearRect(0,0,this.__w,this.rsi.height);
				}
				if(this.smalayer){
					this.smalayer.width = this.__w;
					this.smalayer.getContext('2d').clearRect(0,0,this.__w,this.__h);
					this.smaCtx = null;				
				}											
			},
			generateNumber : function(unit, num){
				var digit = "", index;
				index = 0 > num ? Math.abs(num) - 1 : Math.abs(num);
				for(var i = 0; i < index; i++){
					digit += '0';
				}
				return 0 >num ? Number("0." + digit + ("" + unit)) : Number("" + unit + digit);
			},
			draw : function(){
				var data = this.settings.data;
				var perd = data.period + data.periodUnit,csvds = data.csvChartDataSet,csvdsArr = csvds.split('\n'),sizeofcsvds =  csvdsArr.length - 1,
				xStep = this.__w / (sizeofcsvds - 1),maxV = 0, minV = Infinity,
				firstRow = csvdsArr[0].split(',');
				this.supportlevel = Number(data.supportLevel);
				this.resistancelevel = Number(data.resistanceLevel);
				this.data = data;
				this.chartData = csvdsArr;
				this.xUnit = xStep;
				this.chartDataSize = sizeofcsvds;
				this.period = perd;
				this.startTime = this.parseDate(data.startDate);
				this.endTime = this.parseDate(data.endDate);
				//RSI
				if(firstRow[10]){
					csvds.replace(rsi,function($0,$1){
						maxV = Math.max(maxV,$1 - 0);
						minV = Math.min(minV,$1 - 0);
					});
				}
				//Reset RSI if no data of rsi
				if(minV == Infinity && this.rsi){
					//reset RSI
					this.rsi = null;
					this.__h = this.height;
				}
				if(minV != Infinity){
					var top,bottom;
					if(maxV > 10 && maxV < 100){
						top = (Math.floor(maxV/10) + 1) * 10; 
					}else{
						top = 100; //top limit
					}
					bottom = Math.floor(minV/10) * 10;
					if(!this.rsi){
						this.rsi = document.createElement('canvas');
						this.rsi.height = this.settings.RSI['height'];
						this.__h = this.height - this.rsi.height;
						this.canvas.height = this.__h;
						this.rsi.width = this.__w;
						this.rsi.style.position = "absolute";
						this.rsi.style.left = this.left;
						this.rsi.style.top = this.__h + 10 + this.top + "px";
						this.root.appendChild(this.rsi);
					}
					var ctx = this.rsi.getContext('2d');
					ctx.beginPath();
					ctx.strokeStyle= this.settings.gridcolor;
					ctx.strokeRect(0,0,this.__w,this.rsi.height);
					this.RSI['top'] = top;
					this.RSI['bottom'] = bottom;
					this.RSI['rowHigh'] = this.rsi.height/this.RSI.rows;
					this.RSI['step'] = (this.RSI.top - this.RSI.bottom) / this.RSI.rows;
					//draw dotted
					ctx.beginPath();
					ctx.strokeStyle = "red";
					ctx.fillStyle = "red";
					var x = 0, y = this.rsi.height - ((30 - 0 - this.RSI.bottom) * this.RSI.rowHigh/this.RSI.step);
					while(x < this.__w){
						ctx.moveTo(x, y);
						ctx.lineTo(x+10,y);
						x += 20;
					}
					ctx.fillText(30,10,y - 3);
					if(top >= 70){
						x = 0, y = this.rsi.height - ((70 - 0 - this.RSI.bottom) * this.RSI.rowHigh/this.RSI.step);
						while(x < this.__w){
							ctx.moveTo(x,y);
							ctx.lineTo(x+10,y);
							x += 20;
						}
						ctx.fillText(70,10,y - 3);						
					}
					ctx.stroke();							
				}
				this.realHeight = this.__h - this.settings.bottomMargin;
				maxV = 0, minV = Infinity;
				//close max and min
				csvds.replace(rcls,function($0,$1){
					maxV = Math.max(maxV,$1 - 0);
					minV = Math.min(minV,$1 - 0);	
				});
				//SMA exist
				if(firstRow[5]){
					csvds.replace(sam, function($0,$1){
						maxV = Math.max(maxV,$1 - 0);
						minV = Math.min(minV,$1 - 0);
					});
					if(!this.smalayer){
						this.smalayer = document.createElement("canvas");
						this.smalayer.width = this.__w;
						this.smalayer.height = this.__h;
						this.smalayer.style.position = 'absolute';
						this.smalayer.style.left = this.left;
						this.smalayer.style.top = this.top;	
						this.root.appendChild(this.smalayer);
					}
					this.smaCtx = this.smalayer.getContext('2d');
				}
				//support level, resistance level
				maxV = [maxV,this.supportlevel,this.resistancelevel].max();
				minV = [minV,this.supportlevel,this.resistancelevel].min();		
				//#TODO move to as an option				
				this.gridCount = this.realHeight/35;
				//				
				this.max = maxV;
				this.min = minV;			
				//backup the real value of  min and max
				this.minReal = this.min;
				this.maxReal = this.max;
				//
				var dValue = this.max - this.min;
				var stretch = 0 == dValue ? Math.pow(10, Math.floor(Math.log(Math.abs(this.max)) * Math.LOG10E)) / 10 : Math.pow(10,Math.floor(Math.log(Math.abs(dValue)) * Math.LOG10E)) / 10;
				this.max = Math.ceil(this.max / stretch) * stretch + stretch;
				this.min = Math.floor(this.min / stretch) * stretch - stretch;
				if(0 > this.min && 0 <= minV) { this.min = 0; }
				if(0 < this.max && 0 >= maxV) { this.max = 0; }
				dValue = this.max - this.min;
				stretch = Math.pow(10,Math.floor(Math.log(Math.abs(dValue)) * Math.LOG10E)) / 10;
				this.step = Math.ceil(dValue/this.gridCount/stretch) * stretch;
				dValue = Math.pow(10,Math.floor(Math.log(Math.abs(this.step)) * Math.LOG10E));
				dValue = dValue.toExponential(0).split('e');
				var decimal = Number(dValue[1]);
				9 == Number(dValue[0]) && decimal++;
				var num = this.generateNumber(1,decimal);
				var step = Math.ceil(this.step / num);
				5 < step && (step = 10);
				5 >= step && 2 < step && (step = 5);
				this.step = Math.ceil(this.step / (num * step)) * num * step;				
				1 > num ? (this.maxPrecision = Math.abs(Math.log(Math.abs(num)) * Math.LOG10E), this.maxPrecision = Math.round(this.maxPrecision),
					this.step = this.roundTo(this.step,this.maxPrecision + 1)) : this.maxPrecision = 0;
				//reset the min and max
				this.min = this.step * Math.floor(this.min/this.step);
				this.max = this.step * Math.ceil(this.max/this.step);	
				if(0 > this.min && 0 <= minV) { this.min = 0; }
				if(0 < this.max && 0 >= maxV) { this.max = 0; }
				if(1 < this.minReal && 1 < this.max - this.minReal){
					this.minReal = Math.floor(this.minReal);
				}
				var temp = Math.pow(10,Math.floor(Math.log(Math.abs(this.minReal)) * Math.LOG10E));
				if(this.min == 0){
					this.minReal = temp;
				}
				if(this.min == 0 && 1 < this.minReal){
					this.minReal = 1;
				}
				if(this.min > 0 && this.minReal - this.step > 0){
					this.minReal = this.min + this.step < this.minReal ? this.min + this.step : this.min;
				}
				this.max = this.roundTo(this.max, this.maxPrecision);
				this.min = this.roundTo(this.min, this.maxPrecision);				
				this.stepWidth = this.realHeight / (this.max - this.min);
				//in case the resistance level can not display fully
				var rY = this.getCoordinate(this.resistancelevel);
				while(rY <= 15){
					this.max = this.max + this.step;
					this.stepWidth = this.realHeight / (this.max - this.min);
					rY = this.getCoordinate(this.resistancelevel);
				}
				var rowCount = Math.round((this.max - this.min) / this.step) + 1,precision = 0;				
				1 > this.step && -1 < this.step && (step = this.step.toString(), precision = step.indexOf('e-')!=-1 ? Number(step.split('-')[1]) : step.split(".")[1].length);
				if(precision > this.maxPrecision){
					precision = this.maxPrecision;
				}
				this.numberFormatter = {};
				this.numberFormatter.precision = precision;
				this.numberFormatter.decimalSeparator = '.';
				this.numberFormatter.thousandsSeparator = ',';
				this.rows = [];
				for(var i = 0; i < rowCount; i++){
					var row = this.step * i + this.min;
					row = this.roundTo(row, this.maxPrecision + 1);
					var y = this.getCoordinate(row);
					row = this.formatNumber(row,this.numberFormatter);
					this.rows[i] = {
						y : y,
						label : row
					};
				}
				//
				this.drawRows();
				this.colsArr = [];
				this.colsArrTemp = [];
				this.valueAxes = [];				
				this.render();
				this.drawCols();
				if(this.settings.showRS){
					this.drawDottedline('S');
					this.drawDottedline('R');	
				}
			}
		};
		// Give the init function the jQuery prototype for later instantiation
		hChart.api.init.prototype = hChart.api;
		//
		return hChart;
	})();
	window.jsonPadding = jsonPadding;
	window.hChart = hChart;
})(window);